前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

上手 koa2 MySQL 实战开发|博客系列

# 学习目标

学完这篇教程,你将学会:

  • 如果编写 Koa 中间件
  • 通过 @koa/router 实现路由配置
  • 通过 TypeORM 连接和读写 MySQL 数据库(其他数据库都类似)
  • 了解 JWT 鉴权的原理,并动手实现
  • 掌握 Koa 的错误处理机制

# 准备初始代码

我们已经为你准备好了项目的脚手架,运行以下命令克隆我们的初始代码:

git clone https://github.com/poetries/koa-quickstart.git
@前端进阶之旅: 代码已经复制到剪贴板

如果你访问 GitHub 不流畅,可以克隆我们的 Gitee 仓库:

git clone https://github.com/poetries/koa-quickstart.git
@前端进阶之旅: 代码已经复制到剪贴板

然后进入项目,安装依赖:

cd koa-quickstart && npm install
@前端进阶之旅: 代码已经复制到剪贴板

这里我使用了 package-lock.json 确保所有依赖版本一致,如果你用 yarn 安装依赖出现问题,建议删除 node_modules ,重新用 npm install 安装。

# 最简单的 Koa 服务器

创建 src/server.ts ,编写第一个 Koa 服务器,代码如下:

// src/server.ts
import Koa from 'koa';
import cors from '@koa/cors';
import bodyParser from 'koa-bodyparser';

// 初始化 Koa 应用实例
const app = new Koa();

// 注册中间件
app.use(cors());
app.use(bodyParser());

// 响应用户请求
app.use((ctx) => {
  ctx.body = 'Hello Koa';
});

// 运行服务器
app.listen(3000);
@前端进阶之旅: 代码已经复制到剪贴板

整个流程与一个基本的 Express 服务器几乎完全一致:

  • 初始化应用实例 app
  • 注册相关的中间件(跨域 cors 和请求体解析中间件 bodyParser)
  • 添加请求处理函数,响应用户请求
  • 运行服务器
  • 定睛一看,第 3 步中的请求处理函数(Request Handler)好像不太一样。在 Express 框架中,一个请求处理函数一般是这样的:
function handler(req, res) {
  res.send('Hello Express');
}
@前端进阶之旅: 代码已经复制到剪贴板

两个参数分别对应请求对象(Request)和响应对象(Response),但是在 Koa 中,请求处理函数却只有一个参数 ctx (Context,上下文),然后只需向上下文对象写入相关的属性即可(例如这里就是写入到返回数据 body 中)

function handler(ctx) {
  ctx.body = 'Hello Koa';
}
@前端进阶之旅: 代码已经复制到剪贴板

# 运行服务器

我们通过 npm start 就能开启服务器了。可以通过 Curl (或者 Postman 等)来测试我们的 API:

$ curl localhost:3000
Hello Koa
@前端进阶之旅: 代码已经复制到剪贴板

# 大名鼎鼎的“洋葱模型”

你也许从来没有用过 Koa 框架,但很有可能听说过“洋葱模型”,而 Koa 正是洋葱模型的代表框架之一。下面这个图你也许很熟悉了:

不过以个人观点,这个图实在是太像“洋葱”了,反而不太好理解。接下来我们将以更清晰直观的方式来感受 Koa 中间件的设计之美。首先我们来看一下 Express 的中间件是什么样的:

请求(Request)直接依次贯穿各个中间件,最后通过请求处理函数返回响应(Response),非常简单。然后我们来看看 Koa 的中间件是什么样的:

可以看到,Koa 中间件不像 Express 中间件那样在请求通过了之后就完成了自己的使命;相反,中间件的执行清晰地分为「两个阶段」。我们马上来看下 Koa 中间件具体是什么样的。

# Koa 中间件的定义

Koa 的中间件是这样一个函数:

async function middleware(ctx, next) {
  // 第一阶段
  await next();
  // 第二阶段
}
@前端进阶之旅: 代码已经复制到剪贴板

第一个参数就是 Koa Context,也就是上图中贯穿所有中间件和请求处理函数的绿色箭头所传递的内容,里面「封装了请求体和响应体」(实际上还有其他属性,但这里暂时不讲),分别可以通过 ctx.request 和 ctx.response 来获取,以下是一些常用的属性:

ctx.url    // 相当于 ctx.request.url
ctx.body   // 相当于 ctx.response.body
ctx.status // 相当于 ctx.response.status
@前端进阶之旅: 代码已经复制到剪贴板
  • 中间件的第二个参数便是 next 函数,这个熟悉 Express 的同学一定知道它是干什么的:用来把控制权转交给下一个中间件。但是它跟 Express 的 next 函数本质的区别在于,「Koa 的 「「next」」 函数返回的是一个 Promise」,在这个 Promise 进入完成状态(Fulfilled)后,就会去执行中间件中第二阶段的代码。
  • 那么我们不禁要问:这样把中间件的执行拆分为两个阶段,到底有什么好处吗?我们来通过一个非常经典的例子来感受一下:日志记录中间件(包括响应时间的计算)

# 实战:日志记录中间件

让我们来实现一个简单的日志记录中间件 logger ,用于记录每次请求的方法、URL、状态码和响应时间。创建 src/logger.ts ,代码如下:

// src/logger.ts
import { Context } from 'koa';

export function logger() {
  return async (ctx: Context, next: () => Promise<void>) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    console.log(`${ctx.method} ${ctx.url} ${ctx.status} - ${ms}ms`);
  };
}
@前端进阶之旅: 代码已经复制到剪贴板
  • 严格意义上讲,这里的 logger 是一个「中间件工厂函数」(Factory),调用这个工厂函数后返回的结果才是真正的 Koa 中间件。之所以写成一个工厂函数,是因为我们可以通过给工厂函数传参的方式来更好地控制中间件的行为(当然这里的 logger 比较简单,就没有任何参数)。
  • 在这个中间件的第一阶段,我们通过 Date.now() 先获取请求进入的时间,然后通过 await next() 让出执行权,等待下游中间件运行结束后,再在第二阶段通过计算 Date.now() 的差值来得出处理请求所用的时间。

这里通过两个 Date.now() 之间的差值来计算运行时间其实是不精确的,为了获取更准确的时间,建议使用 process.hrtime() 。

然后我们在 src/server.ts 中把刚才的 logger 中间件通过 app.use 注册进去,代码如下:

// src/server.ts
// ...

import { logger } from './logger';

// 初始化 Koa 应用实例
const app = new Koa();

// 注册中间件
app.use(logger());
app.use(cors());
app.use(bodyParser());

// ...
@前端进阶之旅: 代码已经复制到剪贴板

这时候再访问我们的服务器(通过 Curl 或者其他请求工具),应该可以看到输出日志:

关于 Koa 框架本身的内容基本讲完了,但是对于一个比较完整的 Web 服务器来说,我们还需要更多的“武器装备”才能应对日常的业务逻辑。在接下来的部分,我们将通过社区的优秀组件来解决两个关键问题:路由和数据库,并演示如何结合 Koa 框架进行使用。

# 实现路由配置

由于 Koa 只是一个中间件框架,所以路由的实现需要独立的 npm 包。首先安装 @koa/router 及其 TypeScript 类型定义:

$ npm install @koa/router
$ npm install @types/koa__router -D
@前端进阶之旅: 代码已经复制到剪贴板

有些教程使用 koa-router ,但由于 koa-router 目前处于几乎无人维护的状态,所以我们这里使用维护更积极的 Fork 版本 @koa/router

# 路由规划

在这篇教程中,我们将实现以下路由:

  • GET /users :查询所有的用户
  • GET /users/:id :查询单个用户
  • PUT /users/:id :更新单个用户
  • DELETE /users/:id :删除单个用户
  • POST /users/login :登录(获取 JWT Token)
  • POST /users/register :注册用户

# 实现 Controller

在 src 中创建 controllers 目录,用于存放控制器有关的代码。首先是 AuthController ,创建 src/controllers/auth.ts ,代码如下:

// src/controllers/auth.ts
import { Context } from 'koa';

export default class AuthController {
  public static async login(ctx: Context) {
    ctx.body = 'Login controller';
  }

  public static async register(ctx: Context) {
    ctx.body = 'Register controller';
  }
}
@前端进阶之旅: 代码已经复制到剪贴板

然后创建 src/controllers/user.ts,代码如下

// src/controllers/user.ts
import { Context } from 'koa';

export default class UserController {
  public static async listUsers(ctx: Context) {
    ctx.body = 'ListUsers controller';
  }

  public static async showUserDetail(ctx: Context) {
    ctx.body = `ShowUserDetail controller with ID = ${ctx.params.id}`;
  }

  public static async updateUser(ctx: Context) {
    ctx.body = `UpdateUser controller with ID = ${ctx.params.id}`;
  }

  public static async deleteUser(ctx: Context) {
    ctx.body = `DeleteUser controller with ID = ${ctx.params.id}`;
  }
}
@前端进阶之旅: 代码已经复制到剪贴板

注意到在后面三个 Controller 中,我们通过 ctx.params 获取到路由参数 id 。

# 实现路由

然后我们创建 src/routes.ts,用于把控制器挂载到对应的路由上面:

← node基础篇回顾重新认识Koa2 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合